#include "cljmudx10manager.h"
#include "cljmudx10baseapp.h"

/******************************************
* Stores the Information on our GPU on the
* client machine in the two given data fields.
******************************************/
void CDX10Manager::storeGPUInfo(char* pgpuname, int& pmemoryinfo)
{
	strcpy_s(pgpuname, 128, _prop_gpu_desc);
	pmemoryinfo = _prop_gpu_mem;
	return;
}

/******************************************
* Enable our 3D Z Buffer by setting the
* Output Merger's Stencil State.
******************************************/
void CDX10Manager::disableTwoDimMode()
{
	_obj_dx_device->OMSetDepthStencilState(_obj_depth_stencil_state, 1);
	return;
}

/******************************************
* Disable our 3D Z Buffer by setting the
* Output Merger's Stencil State.
******************************************/
void CDX10Manager::enableTwoDimMode()
{
	_obj_dx_device->OMSetDepthStencilState(_obj_depth_stencil_state_off, 1);
	return;
}


//---------------------PUBLIC METHODS-----------------------------------------------------------------------

/******************************************
* Initialise our Direct X Objects using
* DXGI and the Swap Chain.  This process
* Is documented Clearly in the Direct X
* SDK Documentation.
* This method takes:
*  The Screen width, height, whether we
* are using Vertical Sync, the Handle to
* the Win32 Windows, whether we are fullscreen
* the far and near planes of the view frustum
* used in our camera representation.
******************************************/
bool CDX10Manager::initialise()
{
	HRESULT							tinitresult;
	
	IDXGIFactory*					tobj_dxgifactory;
	IDXGIAdapter*					tobj_dxgiadapter;
	IDXGIOutput*					tobj_dxgioutput;
	DXGI_MODE_DESC*					tobj_dxgimodedesc;
	DXGI_ADAPTER_DESC				tobj_dxgiadapterdesc;
	DXGI_SWAP_CHAIN_DESC			tobj_dxgiswapchaindesc;
	
	unsigned int					tdisplaymodecount;
	unsigned int					tnumerator;
	unsigned int					tdenominator;
	unsigned int					tstringlen;

	int								terrcode;
	
	ID3D10Texture2D*				tobj_dx_backbuffer;
	D3D10_VIEWPORT					tobj_dx_viewport;

	D3D10_TEXTURE2D_DESC			tobj_dx_depthbuffdesc;
	D3D10_DEPTH_STENCIL_DESC		tobj_dx_depthstencildesc_on;
	D3D10_DEPTH_STENCIL_DESC		tobj_dx_depthstencildesc_off;
	D3D10_DEPTH_STENCIL_VIEW_DESC	tobj_dx_depthstencilviewdesc;
	D3D10_RASTERIZER_DESC			tobj_dx_rasterizerdesc;
	
	// Create a DirectX graphics interface factory instance
	tinitresult = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&tobj_dxgifactory);
	if(FAILED(tinitresult))
		return false;

	// Use the factory to create an adapter for the primary graphics interface (video card).
	tinitresult = tobj_dxgifactory->EnumAdapters(0, &tobj_dxgiadapter);
	if(FAILED(tinitresult))
		return false;

	// Enumerate the primary adapter output (monitor).
	tinitresult = tobj_dxgiadapter->EnumOutputs(0, &tobj_dxgioutput);
	if(FAILED(tinitresult))
		return false;

	// Get the number of modes that fit the DXGI_FORMAT_R8G8B8A8_UNORM display format for the adapter output (monitor).
	tinitresult = tobj_dxgioutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &tdisplaymodecount, NULL);
	if(FAILED(tinitresult))
		return false;

	// Create a list to hold all the possible display modes for this monitor/video card combination.
	tobj_dxgimodedesc = new DXGI_MODE_DESC[tdisplaymodecount];
	if(!tobj_dxgimodedesc)
		return false;

	// Now fill the display mode list structures.
	tinitresult = tobj_dxgioutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &tdisplaymodecount, tobj_dxgimodedesc);
	if(FAILED(tinitresult))
		return false;

	// Now go through all the display modes and find the one that matches the screen width and height.
	// When a match is found store the numerator and denominator of the refresh rate for that monitor (e.g. 60000/10000)
	unsigned int i;
	for(i=0; i<tdisplaymodecount; i++)
	{
		if(tobj_dxgimodedesc[i].Width == (unsigned int)this->_obj_wnd_app->getBaseApp()->getScreenWidth())
		{
			if(tobj_dxgimodedesc[i].Height == (unsigned int)this->_obj_wnd_app->getBaseApp()->getScreenHeight())
			{
				tnumerator		= tobj_dxgimodedesc[i].RefreshRate.Numerator;
				tdenominator	= tobj_dxgimodedesc[i].RefreshRate.Denominator;
			}
		}
	}

	// Get the adapter (video card) description.
	tinitresult = tobj_dxgiadapter->GetDesc(&tobj_dxgiadapterdesc);
	if(FAILED(tinitresult))
		return false;

	// Store the dedicated video card memory in megabytes.
	this->_prop_gpu_mem = (int)(tobj_dxgiadapterdesc.DedicatedVideoMemory / 1024 / 1024);

	// Convert the name of the video card to a character array and store it.
	terrcode = wcstombs_s(&tstringlen, _prop_gpu_desc, 128, tobj_dxgiadapterdesc.Description, 128);
	if(terrcode != 0)
		return false;

	// Release the display mode list.
	delete [] tobj_dxgimodedesc;
	tobj_dxgimodedesc = 0;

	// Release the adapter output.
	tobj_dxgioutput->Release();
	tobj_dxgioutput = 0;

	// Release the adapter.
	tobj_dxgiadapter->Release();
	tobj_dxgiadapter = 0;

	// Release the factory.
	tobj_dxgifactory->Release();
	tobj_dxgifactory = 0;

	// Initialize the swap chain description.
    ZeroMemory(&tobj_dxgiswapchaindesc, sizeof(tobj_dxgiswapchaindesc));

	// Set to a single back buffer.
    tobj_dxgiswapchaindesc.BufferCount = 1;

	// Set the width and height of the back buffer.
    tobj_dxgiswapchaindesc.BufferDesc.Width = this->_obj_wnd_app->getBaseApp()->getScreenWidth();
    tobj_dxgiswapchaindesc.BufferDesc.Height = this->_obj_wnd_app->getBaseApp()->getScreenHeight();

	// Set regular 32-bit surface for the back buffer.
    tobj_dxgiswapchaindesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

	// Set the refresh rate of the back buffer.
	if(this->_obj_wnd_app->getBaseApp()->isVSyncEnabled())
	{
	    tobj_dxgiswapchaindesc.BufferDesc.RefreshRate.Numerator = tnumerator;
		tobj_dxgiswapchaindesc.BufferDesc.RefreshRate.Denominator = tdenominator;
	}
	else
	{
	    tobj_dxgiswapchaindesc.BufferDesc.RefreshRate.Numerator = 0;
		tobj_dxgiswapchaindesc.BufferDesc.RefreshRate.Denominator = 1;
	}

	// Set the usage of the back buffer.
    tobj_dxgiswapchaindesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

	// Set the handle for the window to render to.
    tobj_dxgiswapchaindesc.OutputWindow = this->_obj_wnd_app->getWindowHandle();

	// Turn multisampling off.
    tobj_dxgiswapchaindesc.SampleDesc.Count = 1;
    tobj_dxgiswapchaindesc.SampleDesc.Quality = 0;
	
	tobj_dxgiswapchaindesc.Windowed = !this->_obj_wnd_app->getBaseApp()->isFullScreen();

	// Set the scan line ordering and scaling to unspecified.
	tobj_dxgiswapchaindesc.BufferDesc.ScanlineOrdering	= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	tobj_dxgiswapchaindesc.BufferDesc.Scaling			= DXGI_MODE_SCALING_UNSPECIFIED;

	// Discard the back buffer contents after presenting.
	tobj_dxgiswapchaindesc.SwapEffect					= DXGI_SWAP_EFFECT_DISCARD;

	// Don't set the advanced flags.
	tobj_dxgiswapchaindesc.Flags = 0;

	// Create the swap chain and the Direct3D device.
	tinitresult = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION, &tobj_dxgiswapchaindesc, &this->_obj_dx_swapchain, &this->_obj_dx_device);
	if(FAILED(tinitresult))
		return false;

	// Get the pointer to the back buffer.
	tinitresult = _obj_dx_swapchain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&tobj_dx_backbuffer);
	if(FAILED(tinitresult))
		return false;

	// Create the render target view with the back buffer pointer.
	tinitresult = _obj_dx_device->CreateRenderTargetView(tobj_dx_backbuffer, NULL, &this->_obj_rdr_tgt_view);
	if(FAILED(tinitresult))
		return false;

	// Release pointer to the back buffer as we no longer need it.
	tobj_dx_backbuffer->Release();
	tobj_dx_backbuffer = 0;

	// Initialize the description of the depth buffer.
	ZeroMemory(&tobj_dx_depthbuffdesc, sizeof(tobj_dx_depthbuffdesc));

	// Set up the description of the depth buffer.
	tobj_dx_depthbuffdesc.Width = this->_obj_wnd_app->getBaseApp()->getScreenWidth();
	tobj_dx_depthbuffdesc.Height = this->_obj_wnd_app->getBaseApp()->getScreenHeight();
	tobj_dx_depthbuffdesc.MipLevels = 1;
	tobj_dx_depthbuffdesc.ArraySize = 1;
	tobj_dx_depthbuffdesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	tobj_dx_depthbuffdesc.SampleDesc.Count = 1;
	tobj_dx_depthbuffdesc.SampleDesc.Quality = 0;
	tobj_dx_depthbuffdesc.Usage = D3D10_USAGE_DEFAULT;
	tobj_dx_depthbuffdesc.BindFlags = D3D10_BIND_DEPTH_STENCIL;
	tobj_dx_depthbuffdesc.CPUAccessFlags = 0;
	tobj_dx_depthbuffdesc.MiscFlags = 0;

	// Create the texture for the depth buffer using the filled out description.
	tinitresult = _obj_dx_device->CreateTexture2D(&tobj_dx_depthbuffdesc, NULL, &this->_obj_depth_stencil_buff);
	if(FAILED(tinitresult))
		return false;

	// Initialize the description of the stencil state.
	ZeroMemory(&tobj_dx_depthstencildesc_on, sizeof(tobj_dx_depthstencildesc_on));

	// Set up the description of the stencil state.
	tobj_dx_depthstencildesc_on.DepthEnable			= true;
	tobj_dx_depthstencildesc_on.DepthWriteMask		= D3D10_DEPTH_WRITE_MASK_ALL;
	tobj_dx_depthstencildesc_on.DepthFunc			= D3D10_COMPARISON_LESS;
	tobj_dx_depthstencildesc_on.StencilEnable		= true;
	tobj_dx_depthstencildesc_on.StencilReadMask		= 0xFF;
	tobj_dx_depthstencildesc_on.StencilWriteMask	= 0xFF;

	// Stencil operations if pixel is front-facing.
	tobj_dx_depthstencildesc_on.FrontFace.StencilFailOp			= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_on.FrontFace.StencilDepthFailOp	= D3D10_STENCIL_OP_INCR;
	tobj_dx_depthstencildesc_on.FrontFace.StencilPassOp			= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_on.FrontFace.StencilFunc			= D3D10_COMPARISON_ALWAYS;

	// Stencil operations if pixel is back-facing.
	tobj_dx_depthstencildesc_on.BackFace.StencilFailOp			= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_on.BackFace.StencilDepthFailOp		= D3D10_STENCIL_OP_DECR;
	tobj_dx_depthstencildesc_on.BackFace.StencilPassOp			= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_on.BackFace.StencilFunc			= D3D10_COMPARISON_ALWAYS;

	// Create the depth stencil state.
	tinitresult = _obj_dx_device->CreateDepthStencilState(&tobj_dx_depthstencildesc_on, &this->_obj_depth_stencil_state);
	if(FAILED(tinitresult))
		return false;

	// Set the depth stencil state on the D3D device.
	_obj_dx_device->OMSetDepthStencilState(_obj_depth_stencil_state, 1);

	// Initialize the depth stencil view.
	ZeroMemory(&tobj_dx_depthstencilviewdesc, sizeof(tobj_dx_depthstencilviewdesc));

	// Set up the depth stencil view description.
	tobj_dx_depthstencilviewdesc.Format						= DXGI_FORMAT_D24_UNORM_S8_UINT;
	tobj_dx_depthstencilviewdesc.ViewDimension				= D3D10_DSV_DIMENSION_TEXTURE2D;
	tobj_dx_depthstencilviewdesc.Texture2D.MipSlice			= 0;

	// Create the depth stencil view.
	tinitresult = _obj_dx_device->CreateDepthStencilView(this->_obj_depth_stencil_buff, &tobj_dx_depthstencilviewdesc, &this->_obj_depth_stencil_view);
	if(FAILED(tinitresult))
		return false;

	// Bind the render target view and depth stencil buffer to the output render pipeline.
	_obj_dx_device->OMSetRenderTargets(1, &_obj_rdr_tgt_view, _obj_depth_stencil_view);

	// Setup the raster description which will determine how and what polygons will be drawn.
	tobj_dx_rasterizerdesc.AntialiasedLineEnable	= false;
	tobj_dx_rasterizerdesc.CullMode					= D3D10_CULL_BACK;
	tobj_dx_rasterizerdesc.DepthBias				= 0;
	tobj_dx_rasterizerdesc.DepthBiasClamp			= 0.0f;
	tobj_dx_rasterizerdesc.DepthClipEnable			= true;
	tobj_dx_rasterizerdesc.FillMode					= D3D10_FILL_SOLID;
	tobj_dx_rasterizerdesc.FrontCounterClockwise	= false;
	tobj_dx_rasterizerdesc.MultisampleEnable		= false;
	tobj_dx_rasterizerdesc.ScissorEnable			= false;
	tobj_dx_rasterizerdesc.SlopeScaledDepthBias		= 0.0f;

	// Create the rasterizer state from the description we just filled out.
	tinitresult = _obj_dx_device->CreateRasterizerState(&tobj_dx_rasterizerdesc, &this->_obj_raster_state);
	if(FAILED(tinitresult))
		return false;

	// Now set the rasterizer state.
	_obj_dx_device->RSSetState(this->_obj_raster_state);

	// Setup the viewport for rendering.
    tobj_dx_viewport.Width = this->_obj_wnd_app->getBaseApp()->getScreenWidth();
    tobj_dx_viewport.Height = this->_obj_wnd_app->getBaseApp()->getScreenHeight();
    tobj_dx_viewport.MinDepth = 0.0f;
    tobj_dx_viewport.MaxDepth = 1.0f;
    tobj_dx_viewport.TopLeftX = 0;
    tobj_dx_viewport.TopLeftY = 0;

	// Create the viewport.
    _obj_dx_device->RSSetViewports(1, &tobj_dx_viewport);

	// Clear the second depth stencil state before setting the parameters.
	ZeroMemory(&tobj_dx_depthstencildesc_off, sizeof(tobj_dx_depthstencildesc_off));

	// Now create a second depth stencil state which turns off the Z buffer for 2D rendering.  The only difference is 
	// that DepthEnable is set to false, all other parameters are the same as the other depth stencil state.
	tobj_dx_depthstencildesc_off.DepthEnable					= false;
	tobj_dx_depthstencildesc_off.DepthWriteMask					= D3D10_DEPTH_WRITE_MASK_ALL;
	tobj_dx_depthstencildesc_off.DepthFunc						= D3D10_COMPARISON_LESS;
	tobj_dx_depthstencildesc_off.StencilEnable					= true;
	tobj_dx_depthstencildesc_off.StencilReadMask				= 0xFF;
	tobj_dx_depthstencildesc_off.StencilWriteMask				= 0xFF;
	tobj_dx_depthstencildesc_off.FrontFace.StencilFailOp		= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_off.FrontFace.StencilDepthFailOp	= D3D10_STENCIL_OP_INCR;
	tobj_dx_depthstencildesc_off.FrontFace.StencilPassOp		= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_off.FrontFace.StencilFunc			= D3D10_COMPARISON_ALWAYS;
	tobj_dx_depthstencildesc_off.BackFace.StencilFailOp			= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_off.BackFace.StencilDepthFailOp	= D3D10_STENCIL_OP_DECR;
	tobj_dx_depthstencildesc_off.BackFace.StencilPassOp			= D3D10_STENCIL_OP_KEEP;
	tobj_dx_depthstencildesc_off.BackFace.StencilFunc			= D3D10_COMPARISON_ALWAYS;

	// Create the state using the device.
	tinitresult = _obj_dx_device->CreateDepthStencilState(&tobj_dx_depthstencildesc_off, &this->_obj_depth_stencil_state_off);
	if(FAILED(tinitresult))
		return false;
    
	//Unbelievably, we have finally created a DirectX 10 Hardware Accelerated Window and all associated buffers!!!
	//Let's celebrate by returning true, in a little moment of glory!!
	return true;
}

/******************************************
* Cleanup all Dynamically Assigned Direct
* X Objects.  The order is important, we must
* hierarchically cleanup our objects in 
* a bottom-up fashion.
******************************************/
void CDX10Manager::cleanup()
{
	//Revert to windowed mode before killing off the swap chain.
	if(_obj_dx_swapchain)
		_obj_dx_swapchain->SetFullscreenState(false, NULL);

	//If we have a Depth Stencil State, then release it
	if(_obj_depth_stencil_state_off)
	{
		_obj_depth_stencil_state_off->Release();
		_obj_depth_stencil_state_off = NULL;
	}

	//If we have a Rasterizer State, then release it
	if(_obj_raster_state)
	{
		_obj_raster_state->Release();
		_obj_raster_state = NULL;
	}

	//If we have a depth stencil view, then release it.
	if(_obj_depth_stencil_view)
	{
		_obj_depth_stencil_view->Release();
		_obj_depth_stencil_view = NULL;
	}

	//If we have a depth stencil state, then release it.
	if(_obj_depth_stencil_state)
	{
		_obj_depth_stencil_state->Release();
		_obj_depth_stencil_state = NULL;
	}

	//If we have depth stencil buffer, then release it.
	if(_obj_depth_stencil_buff)
	{
		_obj_depth_stencil_buff->Release();
		_obj_depth_stencil_buff = NULL;
	}

	//If we have a render target view, then release it.
	if(_obj_rdr_tgt_view)
	{
		_obj_rdr_tgt_view->Release();
		_obj_rdr_tgt_view = NULL;
	}

	//If we have a dxgi swapchain, then release it.
	if(_obj_dx_swapchain)
	{
		_obj_dx_swapchain->Release();
		_obj_dx_swapchain = NULL;
	}

	//If we have a direct X device object, then release it (must be the last thing to release).
	if(_obj_dx_device)
	{
		_obj_dx_device->Release();
		_obj_dx_device = NULL;
	}
	return;
}

/******************************************
* Begin Rendering the Scene, Refreshing the
* Screen to the Given Colour.
******************************************/
void CDX10Manager::beginRendering()
{
	// Clear the back buffer.
	_obj_dx_device->ClearRenderTargetView(_obj_rdr_tgt_view,_obj_wnd_app->getBaseApp()->getColour());
    
	// Clear the depth buffer.
	_obj_dx_device->ClearDepthStencilView(_obj_depth_stencil_view, D3D10_CLEAR_DEPTH, 1.0f, 0);
	return;
}

/******************************************
* End Rendering the Scene by presenting the
* Back buffer to the Swap Chain.
******************************************/
void CDX10Manager::endRendering()
{
	// Present the back buffer to the screen since rendering is complete.
	if(this->_obj_wnd_app->getBaseApp()->isVSyncEnabled())
		_obj_dx_swapchain->Present(1, 0);
	else
		_obj_dx_swapchain->Present(0, 0);
	return;
}
